package controllers

import (
	"context"
	"fmt"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	v1alpha12 "gitverse.ru/synapse/kubelatte/pkg/api/v1alpha1"
	"gitverse.ru/synapse/kubelatte/pkg/logs"
	"gitverse.ru/synapse/kubelatte/pkg/operator/controllers/mocks"
	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/event"
	"sync"
	"testing"
)

func TestNamespaceReconciler_Reconcile(t *testing.T) {
	logs.Logger = zap.NewNop()
	ctx := context.Background()
	req := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test", Name: "test"}}
	mClient := mocks.NewClient(t)
	mStatusWriter := mocks.NewSubResourceWriter(t)
	mStatusWriter.On("Update", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
	mClient.On("List", mock.Anything, mock.Anything).Return(nil).Maybe()
	mClient.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
	mClient.On("Status").Return(mStatusWriter).Maybe()

	type fields struct {
		Client              client.Client
		Scheme              *runtime.Scheme
		once                sync.Once
		templateObj         v1alpha12.Trigger
		deletedNamespaceObj corev1.Namespace
		initedFlag          bool
	}
	type args struct {
		ctx context.Context
		req ctrl.Request
	}
	tt := struct {
		name    string
		fields  fields
		args    args
		want    ctrl.Result
		wantErr assert.ErrorAssertionFunc
	}{
		name:   "Test reconcile",
		fields: fields{Client: mClient, once: sync.Once{}, initedFlag: true},
		args: args{
			ctx: ctx,
			req: req,
		},
		wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
			assert.True(t, err != nil)
			return err == nil
		},
	}
	t.Run(tt.name, func(t *testing.T) {
		r := &NamespaceReconciler{
			Client:     tt.fields.Client,
			once:       sync.Once{},
			initedFlag: tt.fields.initedFlag,
		}
		_, err := r.Reconcile(tt.args.ctx, tt.args.req)
		if !tt.wantErr(t, err, fmt.Sprintf("Reconcile(%v, %v)", tt.args.ctx, tt.args.req)) {
			return
		}
	})
}

func TestNamespaceReconciler_manageEvents(t *testing.T) {
	mClient := mocks.NewClient(t)
	mClient.On("List", mock.Anything, mock.Anything).Return(nil).Maybe()
	mClient.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return().Maybe()

	type fields struct {
		Client          client.Client
		Scheme          *runtime.Scheme
		once            sync.Once
		templateObj     v1alpha12.Scope
		deletedScopeObj v1alpha12.Scope
		initedFlag      bool
	}
	tt := struct {
		name   string
		fields fields
	}{
		name:   "Test manageEvents",
		fields: fields{Client: mClient, Scheme: nil, once: sync.Once{}, templateObj: v1alpha12.Scope{}, deletedScopeObj: v1alpha12.Scope{}, initedFlag: true},
	}
	t.Run(tt.name, func(t *testing.T) {
		r := &NamespaceReconciler{
			Client:     tt.fields.Client,
			Scheme:     tt.fields.Scheme,
			once:       sync.Once{},
			initedFlag: tt.fields.initedFlag,
		}
		r.manageEvents()
	})

	ns := corev1.Namespace{
		TypeMeta:   v1.TypeMeta{Kind: "Namespace", APIVersion: "v1"},
		ObjectMeta: v1.ObjectMeta{Name: "nsns", Namespace: "test", Generation: 0, ResourceVersion: "0"},
		Spec:       corev1.NamespaceSpec{},
		Status:     corev1.NamespaceStatus{Phase: "running"},
	}
	ns1 := corev1.Namespace{
		TypeMeta:   v1.TypeMeta{Kind: "Namespace", APIVersion: "v1"},
		ObjectMeta: v1.ObjectMeta{Name: "snsnns", Namespace: "lala", Generation: 1, ResourceVersion: "1"},
		Spec:       corev1.NamespaceSpec{},
		Status:     corev1.NamespaceStatus{Phase: "running"},
	}

	r := NamespaceReconciler{}
	p := r.manageEvents()

	// Create test
	assert.True(t, p.Create(event.CreateEvent{Object: &ns}))

	// Update test
	assert.True(t, p.Update(event.UpdateEvent{ObjectNew: &ns, ObjectOld: &ns1}))
	assert.False(t, p.Update(event.UpdateEvent{ObjectNew: &ns, ObjectOld: &ns}))

	//Delete test
	assert.True(t, p.Delete(event.DeleteEvent{Object: &ns}))

}

func TestNamespaceReconciler_HealthCheck(t *testing.T) {
	t.Run("test", func(t *testing.T) {
		r := &NamespaceReconciler{
			initedFlag: false,
		}
		assert.Equalf(t, false, r.HealthCheck(), "HealthCheck()")
	})
}
